package com.chapter.sample.answer.multiplex;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

/**
 * 线程池抛出异常，如何处理
 */
@Slf4j
public class ThreadPoolExceptionDemo {
    public static void main(String[] args) throws InterruptedException {
        //defaultSubmit(); 1、默认submit会吞掉异常
        //defaultSumbitAndGet();2、submit执行后，如果get方法调用想要获得返回值，会抛出异常；如果不get，也是会吞掉异常
        //defaultExecute();3、execute默认会抛出异常
        //===============
        //handleException();4、统一submit/execute异常机制，ThreadPoolExecutor提供了afterExecute，重新这个方法
    }

    private static void handleException() throws InterruptedException {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                Runtime.getRuntime().availableProcessors(),
                Runtime.getRuntime().availableProcessors() * 2,
                1L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100)) {
            @Override
            protected void afterExecute(Runnable runnable, Throwable throwable) {
                //execute运行
                if (throwable != null) {
                    log.error(throwable.getMessage(), throwable);
                }
                //submit运行
                if (throwable == null && runnable instanceof Future<?>) {
                    try {
                        Future<?> future = (Future<?>) runnable;
                        if (future.isDone()) {
                            future.get();
                        }
                    } catch (CancellationException ce) {
                        throwable = ce;
                        ce.printStackTrace();
                    } catch (ExecutionException ee) {
                        throwable = ee;
                        ee.printStackTrace();
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                        Thread.currentThread().interrupt();
                    }
                }
            }
        };


        //submit会吞掉异常
        threadPool.submit(() -> {
            //默认submit方法（不获取返回值）会吞掉异常，改写后会抛出异常了
            System.out.println(Thread.currentThread().getName() + "\t" + "进入池中的submit方法");
            int age = 10 / 0;
            System.out.println(Thread.currentThread().getName() + "\t" + "进入池中的submit方法 - end");
        });

        System.out.println();
        TimeUnit.SECONDS.sleep(1);
        System.out.println();


        //3、execute执行后，会抛出异常
        threadPool.execute(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "进入池中的execute方法");
            for (int i = 1; i <= 4; i++) {
                if (i == 3) {
                    int age = 10 / 0;
                }
                System.out.println("---come in execute：" + i);
            }
            System.out.println(Thread.currentThread().getName() + "\t" + "进入池中的execute方法 - end");
        });

    }

    //3、execute 默认会抛出异常
    private static void defaultExecute() {
        {
            ExecutorService threadPool = Executors.newFixedThreadPool(2);
            try {
                //3、execute执行后，会抛出异常
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t" + "进入池中的submit方法");
                    for (int i = 1; i <= 4; i++) {
                        if (i == 3) {
                            int age = 10 / 0;
                        }
                        System.out.println("---come in execute：" + i);
                    }
                    System.out.println(Thread.currentThread().getName() + "\t" + "进入池中的submit方法 - end");
                });
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                finalOK_shutdownAndAwaitTermination(threadPool);
            }
        }
    }

    //2、submit执行后，如果get方法调用想要获得返回值，会抛出异常
    private static void defaultSubmitAndGet() {
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        try {
            //2、submit执行后，如果不获取返回值submit会吞掉异常，但如果get方法调用想要获得返回值，会抛出异常
            Future<?> result = threadPool.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "\t" + "进入池中的submit方法");
                for (int i = 1; i <= 4; i++) {
                    if (i == 3) {
                        int age = 10 / 0;
                    }
                    System.out.println("---come in execute：" + i);
                }
                System.out.println(Thread.currentThread().getName() + "\t" + "进入池中的submit方法 - end");
            });
            result.get();//如果没有这一行，异常吞掉
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }

    //1、默认会吞掉异常
    private static void defaultSubmit() {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        try {
            //submit会吞掉异常
            threadPool.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "\t" + "进入池中的submit方法");
                for (int i = 1; i <= 4; i++) {
                    if (i == 3) {
                        int age = 10 / 0;
                    }
                    System.out.println("---come in execute：" + i);
                }
                System.out.println(Thread.currentThread().getName() + "\t" + "进入池中的submit方法 - end");
            });
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }

    /**
     * 停机分2个阶段,
     * 1、shutdown一后就拒绝了新进入的任务，
     * 启动一个有序的关闭程序，再这个过程中，之前已经提交的任务将被执行,但不会接受新任务.如果系统已经关闭,那么再次调用此方法将不回产生额外的效果
     * 不会再接受新任务的提交
     * 再shutdown调用之前的任务会被执行下去,待执行的任务和正在执行的任务都不会取消,将继续执行
     * 如果已经shutdown了,再调用不会有其他影响
     * 2、然后再shutdownNow，然后去取消正在执行的任务
     *
     * @param threadPool
     */
    public static void finalOK_shutdownAndAwaitTermination(ExecutorService threadPool) {
        if (threadPool != null && !threadPool.isShutdown()) {
            threadPool.shutdown();
            try {

                if (!threadPool.awaitTermination(120, TimeUnit.SECONDS)) {
                    threadPool.shutdownNow();
                    if (!threadPool.awaitTermination(123, TimeUnit.SECONDS)) {
                        System.out.println("pool dit not terminate");
                    }
                }
            } catch (Exception e) {
                threadPool.shutdown();
                Thread.currentThread().interrupt();
            }
        }
    }
}
